Laravel 4.2で書かれたアプリをLaravel 5.8にする
Laravelは4と5でアーキテクチャが大きく変更された
既存プロジェクトの移行の際につまづきそうな点をまとめる
(長大な文章だが分割していないのは一括で検索できるのが便利なため)
基本的な考え方
基本的にUpgrade Guideをなぞるだけ
基礎知識
Laravelのバージョン
2019年3月時点でLaravelの最新版は5.8
アップグレードの方針
もし4系の最新4.2のプロジェクトを5.7にあげたいなら
公式のおすすめは4.2->5.0にアップグレードした後にUpgrading guideにそってどんどんあげていく方法っぽい
同じ5系でもマイナーバージョンがあがるごとにUpgradeの手順が必要になる
他の手段としては、composerでlaravel自体をあげてしまい、エラーを潰していく方法がある
Laravelのアプリケーションがどのように初期化されるか
Laravel 5 のディレクトリ構成
Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class.
自由に配置できる
FilterはMiddlewareになった
Filter消えた
4.2の実態はこれ
5.1でdeprecatedになった(5.2では削除)
Middleware増えた
書き方はこうかわる
4.2まではFilterだった
code:php
// ユーザページの表示をするときに認証をかましている部分のルーティング
Route::get('/users/me', [
'before' => [
'auth', // ユーザ情報を返す前に権限チェックしたい
],
'uses' => UserController::class . '@show',
]);
5.7はmiddlewareを使って同じことができる
code:php
Route::get('/users/me', [
'uses' => UserController::class . '@show',
])->middleware('auth');
Q. 以前はbeforeと指定していたのに、これじゃあbeforeなのかafterなのかわからないのでは?
A. Yes. Routeファサードを見るだけではわからなくなった
Afterに複数個設定することはできなくなった?(そもそもやらなそう)kadoyau.icon
Controllerの名前空間はデフォルトではApp\Http\Controllers
Since the RouteServiceProvider loads your route files within a route group that contains the namespace, we only specified the portion of the class name that comes after the App\Http\Controllers portion of the namespace.
問題
Route::get('foo', 'Photos\AdminController@method')のようにルーティングを書くと、デフォルトのコントローラの名前空間が自動的に付与されてApp\Http\Controllers\Photos\AdminControllerを読みに行く
4.2のアプリはディレクトリ構成が違うはずなので死ぬ
対応
RouteServiceProvidersの上記変数$namespaceをつけたい名前空間に書き換えるだけ
何もつけたくないならコメントアウトすればいい
データベース(MySQL)
「DBいじってないのにerror invalid datetime format: 1292 Incorrect datetime value: '0000-00-00 00:00:00'とかでるんだけど/icons/はて?.icon」
原因
set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
set sessionはセッション中のみに有効な変数の設定
対応
1. (根本対応)スキーマ定義をなおす
クエリログはデフォルトではoffになった
DBから取り出した値がObjectになる
FetchモードがObject固定になっています
Laravel 4だとconfigで指定できていました
code:database.php
'fetch' => PDO::FETCH_ASSOC
Laravel 5ではIlluminate\Database\Events\StatementPreparedイベントをフックして変更することができます
素直にobjを使うように書き換えるのがよさそうkadoyau.icon
KVS(Redis)
predisがない
原因
対応
composerでアプリケーションに明示的に入れる
Redis::connection()の返り値の型が変更されている
原因
4.2では\Predis\ClientInterfaceを返していた
5.7ではIlluminate\Redis\Connections\Connectionが返る
対応
RedisManagerはclientを持っているので単にプロパティを呼び出せばよい
4.2 Redis::connection()
5.7 Redis::connection()->client
キューの取扱がかなり変わった
設定値
Config::get()で取得できる設定値の設定はさほど変わっていない
4.2 app/config/app.phpのこの設定値にアクセスしたいとき Config::get('app.timezone')
5.7 config/app.phpのこの設定値にアクセスしたいとき Config::get('app.name')
$app['config']['hoge']で取れるものはconfig/hoge.phpで返しているということ
ConfigのServiceProviderで初期設定される
環境に左右される箇所はenv()を利用して読み込む
code:php
'connections' => [
'mysql' => [
'password' => env('DB_PYASSWORD', '') // local, development, productionで.envに記載される情報が異なるのでenv()で取得する
]
]
秘密の設定を入れたいときは環境変数を使うが、アクセス方法が変わった
4.2 Laravel独自の.envの仕組みを使っていた
.envに秘匿情報を書く
code:.env
// key=value 形式で秘匿情報を書く
DB_PASSWORD=SUPERSTRONGPASS
.envファイルは何らかの方法で環境ごとにデプロイするkadoyau.icon
ビュー
テンプレートに値を渡したいとき
code:php
Route::get('/', function()
{
// app/views/greeting.phpに変数を渡す
return View::make('greeting', array('name' => 'Taylor'));
});
code:php
Route::get('/', function () {
// resources/views/greeting.blade.phpに変数を渡す
});
table:viewの違い
Laravelのバージョン 4.2 5.7
デフォルトパス app/views resources/views
デフォルトパスの設定値 app/config/view.php config/view.php
エスケープの挙動
4.2 {{ /* エスケープされない */ }}
5.7は同じ記法で、デフォルトでエスケープ(htmlspecialchars())される {!! /* 5.7ではこれでエスケープされない */ !!}
ルーティング
4.2->5.0ではroutes.phpは単にディレクトリを移動するだけ
アプリケーションのbootの際にRoutingServiceProviderあたりがここを読むようになっている
5.3以降routes.phpは消えた
例外に応じて返すレスポンス
例外に対してどのようなハンドリングをするかをapp/start/global.phpの中で指定していた
code:exceptionHandlerConfig.php
App::error(function(Exception $exception) { Log::error($exception); });
App::error(function(InvalidUserException $exception){ return createResponseForInvalidUserException } // 例外ごとにviewを変更する
App::missing(function(){}) // 404エラーのハンドリング
App::down(function(){})
App::missing()で指定する関数はNotForundHttpExceptionが投げられたときに呼ばれる App::error のAPIリファレンス
5.7にはerrorなどのメソッドは存在しない
App\Exceptions\Handlerが次の役割を持っている
例外を記録&ログを外部サービスに投げるreport()
例外発生時にユーザへのエラー表示(HTTPレスポンス)render()
Handlerクラスでコントロールできること
特定の例外に対してreport()を呼び出したくない(ログを記録したくない)
dontReport配列に例外のクラス名を指定する
validadationの例外でflashしたくないキーを指定する
kadoyau.icon 多分例外ハンドリング時にflushされるのだろう。入力されたpasswordをviewから消したくないとかにつかいそう
dontFlash配列に例外タイプを指定する
Laravel 4の記法との比較
例外によってブラウザにメッセージを出し分けたいとき
code:php
// App::error(function(InvalidUserException $exception){ return createResponseForInvalidUserException } の代わり
public function render($request, Exception $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
// App::missing()の代わり
if ($exception instanceof NotFoundHttpException) {
retuern createResponse(); // HTTPレスポンスは自由に作る
}
APP_NAMEの影響箇所
session cookieの名前が変更になる
cacheのprefix
テスト
テスト環境であることをフレームワークに設定する方法
4.2
5.7
phpunit.xmlで設定する
ただし、このあとにphp artisan config:cacheをする必要がある
なぜハードコードなのか聞いている人がいるが、標準的な場所だからとしてcloseされている
未整理
アプリケーションの初期化スクリプトが抽象化された
全体的にベタ書きの印象があるlaravel4 の初期化スクリプトは、laravel5になって大幅に整理された
アプリケーションのカーネルがリクエストを受け取ってレスポンスを返す形に抽象化された
ユーザは気に食わなければカーネルを差し替えることができる
リクエストをハンドルする際に、Service Providersなどで初期化が行われる
なのでここで色々独自な処理をしていると全てうまくいかなくなった
特に重要なものはフレームワークのApplication基底クラスで初期化されるので、そこに手を入れたいという要件ではFWのコードをforkすることになる
Laravelにのっかることを検討してください
forkしたくないよね?未来にも同じ苦しみが待ってる。今なら間に合う
フレームワークのコードは5のほうがスッキリ抽象化されていて綺麗kadoyau.icon
変更点
bootstrap/autoload.php -> public/index.php
bootstrap/start.php -> bootstrap/app.php
5系のマイナーアップデートで影響が大きそうなもの
アップグレードマニュアルから影響が大きそうなものを抜粋する
ただし、実際に作業する際にはマニュアル全部眺めるべき
特に、アップグレードマニュアルの「非推奨」はハマるので全部読むこと
4.2から5.7にあげようとしている場合、5.1で非推奨、5.2で消えたみたいなものがあるとハマる
アップグレード時間の見積もり:10分から15分
cacheのディレクトリがstorage/framework/cacheになったのでディレクトリを作成する必要がある
Route::redirectは302を返すようになった
見積もり時間:10〜30分
Symfony 4系
PHPUnit 7系
ログの設定はconfig/logging.phpになった
5.5のconfig/app.phpに記述されていたこの設定は削除された
Illuminate\Log\Writerクラスは、Illuminate\Log\Loggerに名前が変更
Log::getMonolog()は廃止
アップデートの見積もり時間:1時間
セキュリティ
クッキーのシリアライズが無効に
Artisanコマンド
fire() -> handle()へリネーム
アップグレード見積もり時間 1〜2時間
bootstrap/cache/compiled.phpの廃止
コレクション
collectionのメソッドがrenameされた
コンテナ
bind()の第一引数にエイリアスが登録できなくなった
make()の第二引数に配列を受け取らなくなった
テスト
laravel5.4テストレイヤを使用して書かれた新しいテストは、TestCaseクラスを拡張します
テスト名前空間を作れ
読み込んだ.envファイルのAPP_ENV変数を使用します。
キュー
failed_jobsテーブルを使ったらカラムを追加する必要がある。やらないとジョブが失敗したときにログの挿入ができなくなる(fatalになる)
アップデート見積もり時間:2時間から3時間
キュー
configのttrがretry_afterに
In your queue configuration, all expire configuration items should be renamed to retry_after. Likewise, the Beanstalk configuration's ttr item should be renamed to retry_after. This name change provides more clarity on the purpose of this configuration option.
queue:workの際に--daemonが不要に
ジョブがデータの情報を持たなくなった
$event->job->payload()で取得する必要がある
Queue::failing
キュー
タイムアウトの設定をしないと、実行時間のかかるjobがある場合に(デフォルトのタイムアウト60秒にひっかかって)必ず失敗する
データベース
クエリビルダがArrayではなくIlluminate\Support\Collectionをかえすようになった
次のようなコードで突然死ぬ
code:php
// いままでArrayが帰っていたのにCollectionが帰ってくるようになるメソッド
public function fetchUserData() {
$rows = DB::table('table_name')->where($user_id)->limit(5);
return array_map(function($row){
return $this->dataProsessing($row);
}, $rows);
}
対応
mapでarrayにキャストするとかする
アップデートにかかる時間の見積もり:1時間以下
認証
config/auth.phpが新しくなった
Auth::extendを使っていたらAuth::providerに書き換えた上でapp.phpのprovidersに追記する
環境変数
app.phpのenvにデフォルト値が追加された'env' => env('APP_ENV', 'production'),
サービスプロバイダーの廃止
app.phpから次のプロバイダを消す
Illuminate\Foundation\Providers\ArtisanServiceProvider
Illuminate\Routing\ControllerServiceProvider
App\Providers\BusServiceProvider(5.0.16)
App\Providers\ConfigServiceProvider(5.0.16)
bootstrap/autoload.phpの変更
$compiledPath = __DIR__.'/../vendor/compiled.php';(5.0.16)
非推奨
Illuminate\Contracts\Routing\Middlewareが非推奨に
データベース
MySQLのデフォルトモードの変更
MySQL5.7以降では、strictモードがデフォルトで有効になったため、0000-00-00 00:00:00が有効な日付として取り扱われなくなりました。
lists()メソッドがpluck()にrenameされた
The lists method on the Collection, query builder and Eloquent query builder objects has been renamed to pluck. The method signature remains the same.
アップデートにかかる時間の見積もり:1時間以下
認可とポリシーの追加(オプショナル)
app/Commandsディレクトリーはapp/Jobsへ名前が変更されました。しかし、コマンドを全部新しい場所へ移動する必要はありませんし、make:commandとhandler:command Artisanコマンドも続けて使用できます。
同様に、app/Handlersディレクトリーはapp/Listenersに名前が変更され、イベントリスナーだけが含まれるようになりました。しかし、既に存在するコマンドとイベントハンドラーを移動したり、名前を変えたりする必要はありません。handler:eventコマンドもイベントハンドラーを生成するために続けて使用できます。
テスト
tests/TestCase.phpファイルにprotectedの$baseUrlプロパティーを追加してください。
PHPのmcrypt extensionは不要になり、openssl extensionが利用されるようになった
4.2 -> 5.0
Artisan コマンド
5.2でmake:consoleからmake:commandになった
Move all of your command classes from your old app/commands directory to the new app/Console/Commands directory. Next, add the app/Console/Commands directory to the classmap directive of your composer.json file
5.7で新規にアプリを作成してもclassmapにapp/Console/Commandは指定されていない Laravel 5.5からCommandもAutoloadされるようになったため
In Laravel 5.5, Artisan can automatically discover commands so that you do not have to manually register them in your kernel. To take advantage of this new feature, you should add the following line to the commands method of your App\Console\Kernel class:
$this->load(__DIR__.'/Commands');
Then, copy your list of Artisan commands from start/artisan.php into the commands array of the app/Console/Kernel.php file.
5.7でphp artisan make:commandでコマンドを作成してもConsole/Kernel.phpの配列には追加されない
環境変数の移行